from none_bot_py_plugins.recv_mod.recv_msg import Receive,RecvAlreadyExistsException,RecvNotExistsException,RecvNotRunningException,RecvOnRunningException
from abc import *
import asyncio
import time


class GenSvrBase:

    def __init__(self, recv_id, debug=False):
        self._recv_obj = self.__create(recv_id)
        self.recv_id = recv_id
        self.__debug = debug

    def task_start(self, *args):
        return asyncio.create_task(self.start(args))

    async def start(self, *args):
        state = self._init_(self, *args)
        self._recv_obj.set_state("state", state)
        await Receive.start_recv(self.recv_id)

    def __create(self, recv_id):
        return Receive.init_recv(recv_id,
                                 {'handle_cast': lambda msg: self.gen_cast_recv(msg),
                                  'handle_call': lambda msg: self.gen_call_recv(msg),
                                  'default': lambda msg: self.gen_info_recv(msg),  # 意外消息
                                  'error': lambda msg: self.gen_err_recv(msg)})

    # 同步消息
    async def call(self, recv_id, msg, time_out = 5):
        now = us_time_stamp()
        key = (self.recv_id, recv_id, now)
        Receive.send_recv_msg(recv_id, ('handle_call', (key, msg)))
        try:
            task = asyncio.create_task(Receive.run_recv(self.recv_id, {key: lambda ret_msg: ret_msg}))
            await asyncio.wait_for(task, time_out)
            ret = task.result()
        except asyncio.TimeoutError:
            if self.__debug:
                print("[", time_stamp(), "] call ", recv_id, "time out!!")
            return None
        return ret

    # 异步消息
    def cast(self, recv_id, msg):
        now = us_time_stamp()
        key = (self.recv_id, recv_id, now)
        Receive.send_recv_msg(recv_id, ('handle_cast', (key, msg)))

    def gen_call_recv(self, recv_msg: tuple):
        key, msg = recv_msg
        from_id, to_id, ts = key
        if to_id != self.recv_id:
            Receive.send_recv_msg(from_id, ('error', ('bad_recv', to_id)))
            return
        result, ret_msg, new_state = self.handle_call(from_id, msg, self._recv_obj.get_state("state"))
        Receive.send_recv_msg(from_id, (key, ret_msg))
        self._recv_obj.set_state("state", new_state)
        self.__catch_stop(result, new_state)

    def gen_cast_recv(self, recv_msg: tuple):
        key, msg = recv_msg
        from_id, to_id, ts = key
        if to_id != self.recv_id:
            Receive.send_recv_msg(from_id, ('error', ('bad_recv', to_id)))
            return
        result, new_state = self.handle_cast(from_id, msg, self._recv_obj.get_state("state"))
        self._recv_obj.set_state("state", new_state)
        self.__catch_stop(result, new_state)

    def gen_info_recv(self, recv_msg):
        result, new_state = self.handle_info(recv_msg, self._recv_obj.get_state("state"))
        self._recv_obj.set_state("state", new_state)
        self.__catch_stop(result, new_state)

    def gen_err_recv(self, err_msg):
        result, new_state = self.handle_error(err_msg, self._recv_obj.get_state("state"))
        self._recv_obj.set_state("state", new_state)
        self.__catch_stop(result, new_state)

    def __catch_stop(self, result, state):
        if type(result) == tuple:
            if result[0] == "stop":
                if len(result) >= 2:
                    self.handle_stop(result[1], state)
                    Receive.stop_recv(self.recv_id, result[1])
                    Receive.del_recv(self.recv_id)
                else:
                    raise BadReplyException

    @abstractmethod
    def handle_cast(self, from_id, msg, state):
        raise NotImplemented

    @abstractmethod
    def handle_info(self, msg, state):
        raise NotImplemented

    @abstractmethod
    def handle_call(self, from_id, msg, state):
        raise NotImplemented

    @abstractmethod
    def handle_error(self, msg, state):
        raise NotImplemented

    @abstractmethod
    def handle_stop(self, result, state):
        raise NotImplemented

    @abstractmethod
    def _init_(self, *args):
        raise NotImplemented


def ms_time_stamp():
    return int(round(time.time() * 1000))


def time_stamp():
    return int(round(time.time()))


def us_time_stamp():
    return int(round(time.time() * 1000000))


class BadReplyException(Exception):
    def __init__(self, *args):
        self.args = args
